CloudWatch Logsの新機能フィールドインデックスでクエリのスキャン量が減ることを確認してみた

CloudWatch Logsの新機能フィールドインデックスでクエリのスキャン量が減ることを確認してみた

追加料金無しでフィールドインデックスが利用可能になりました!今後は利用必須といえる機能なので今すぐ設定しましょう!!
Clock Icon2024.12.15

リテールアプリ共創部@大阪の岩田です。

以前こんなブログを書きました。

https://dev.classmethod.jp/articles/cwlogs-insights-query-scan-volume-not-affect-costs/

CW Logs Insightsのクエリは検索対象の時間範囲指定によってのみスキャン量が決まるといった趣旨のブログ...だったのですが、2024/11/21付けのアップデートでCloudWatch Logsにフィールドインデックスの機能がサポートされました。

https://aws.amazon.com/about-aws/whats-new/2024/11/amazon-cloudwatch-logs-field-indexes-log-group-selection-log-insights/

この機能を使うとクエリの高速化、スキャン量の削減が可能とのことです。さっそくフィールドインデックスについて検証してみました。

フィールドインデックスの概要

まずフィールドインデックスの概要について箇条書きでまとめてみました。

  • 追加料金無しで利用可能!!

  • どのフィールドに対してインデックスを作成するかインデックスポリシーで定義する

  • インデックスポリシーにはアカウントレベルとロググループレベルの2種類がある

    • ロググループレベルのインデックスポリシーはアカウントレベルの設定をオーバーライドする
  • 最大20個のフィールドに対してインデックスが作成可能

  • 指定するフィールド名は大文字/小文字が区別される

  • フィールド名は最大100文字

  • ポリシー削除後も最大30日間はインデックスが利用可能

  • フィールドインデックスは構造化ログとサービスログのみサポートされる

    • サービスログというのはAWSサービスが自動出力するログのことだと思います。例えばLambdaであればSTART RequestId: ...というログなど
  • インデックスポリシー作成後に取り込まれたログのみインデックスされ、ポリシー作成前に取り込んだログに対してはインデックスが作成されない。

  • CW Logs Insightsの新しい構文filterIndexを使うと、指定されたフィールドでインデックスされたロググループとデータのみをスキャンするように強制できる

    • filterIndexは低頻度アクセスログクラスではサポートされない
  • filterIndexではなくfilterを指定した場合はフィールドインデックスを利用するかは自動的に判断される

追加料金無しで利用可能というのが大きいですね。今後は利用必須の機能と言えるでしょう。

やってみる

それでは実際にフィールドインデックスの機能を試していきましょう。主に私が運用しているサンシャイン池崎BotというSlackアプリのログを使ってフィールドインデックスを試してみます。

フィールドインデックス作成前にリクエストIDを指定してログを抽出

まずは普通に12/1 ~ 12/15のログから指定したリクエストIDに一致するログが無いか検索してみます。

fields @timestamp, @message, @logStream, @log
| filter @requestId = "628f1203-3b5e-4c82-b27e-a4809cd46f1e"

結果は以下の通りでした。

3 の 3 の一致したレコードの表示
857 レコード (136.9 KB) が 2.6s @ 329 records/s (52.7 KB/s) でスキャンされました

857レコードがスキャンされています。

フィールドインデックスの作成

ここからが本番です。マネコンから対象のロググループを開くと、「フィールドインデックス」というタブが増えていることが分かります。ここから「フィールドインデックスを作成」をクリックします。

ロググループの設定

「Field path」にインデックスを貼りたいフィールドを指定します。今回は@requestIdを指定しました。

フィールドインデックスの指定

これでフィールドインデックス作成完了です。

フィールドインデックス作成後にリクエストIDを指定してログを抽出

インデックスポリシーを作成しても既存のログにはインデックスが作成されないため、アプリを起動して新しいログを出力させます。ログが出力されたら一旦普通にCW Logsのログを表示してリクエストIDを控えます。

リクエストIDの確認

インデックスポリシー作成後にリクエストIDがe8cce1e5-07e7-4172-b04e-1cc0d69f608eのログが出力されたことが確認できました。このリクエストIDをfilterIndexで指定しつつ、先ほどと同様に時間範囲は12/1 ~ 12/15を指定してクエリを実行します。

fields @timestamp, @message, @logStream, @log
| filterIndex @requestId = "e8cce1e5-07e7-4172-b04e-1cc0d69f608e"

結果は以下の通りでした。

3 の 3 の一致したレコードの表示
4 レコード (669 B) が 2.1s でスキャンされました。フィールドインデックスを利用 

フィールドインデックス作成前と違ってスキャン対象が4レコードに減っています!!さらにフィールドインデックスを利用 の表示も増えています。

さらにログを増やしてから再度同じクエリを実行してみましたが、スキャン量は変わらずでした。

3 の 3 の一致したレコードの表示
4 レコード (669 B) が 2.6s でスキャンされました。フィールドインデックスを利用 

試しに別のリクエストIDを指定してみました。

fields @timestamp, @message, @logStream, @log
| filterIndex @requestId = "9ab8d147-4803-4f8e-ada2-5c1a16d4235a"

スキャン量が78レコードに増えました。

3 の 3 の一致したレコードの表示
78 レコード (11.3 KB) が 2.5s でスキャンされました。フィールドインデックスを利用 

この辺りはインデックスのデータ構造に依存して多少ブレはありそうですね。仮にBツリーインデックスを利用しているのであればブランチノードでデータが見つかったのか、リーフノードでデータが見つかったのかでスキャン量は変わってきそうです。いずれにせよフィールドインデックス作成前と比べると同じ時間範囲指定にも関わらずスキャン量が削減できていることが分かります。

filterを指定した場合にフィールドインデックスが自動で利用されるか確認してみた

ここまでの検証ではクエリにfilterIndexを指定してきましたが、filterを指定した場合の挙動がどうなるかも確認しておきましょう。新しく適当なLambda関数を作成し、ロググループのフィールドインデックスポリシーで@requestId@logStreamをインデックス対象として指定しました。この状態で何度かLambdaをテスト実行し、ログを蓄積しました。

まずログストリーム別のログ件数を確認しておきましょう。

stats count(@logStream) by @logStream

結果は以下の通りでした。

@logStream count(@logStream)
2024/12/15/[$LATEST]b127d2b3ad4048df85ec127be9da5981 217

全てのログが単一のログストリームに保存されていることが分かります。

まずはfilterでリクエストIDを指定したときの動作を確認しました。

fields @timestamp, @message, @logStream, @log
| filter @requestId = "1c8fd092-031c-4d93-bad8-1bbfc8e33203"

結果は以下のとおりです。

3 の 3 の一致したレコードの表示
30 レコード (3.5 KB) が 2.0s でスキャンされました。フィールドインデックスを利用 

フィールドインデックスを利用 と出力されており、自動的にフィールドインデックスが利用されたことが分かります。

続いてログストリームを指定したときの動作を確認してみます。ログストリームは1つだけなのでフィールドインデックスを使うと逆に効率が悪くなります。

fields @timestamp, @message, @logStream, @log
| filter @logStream = "2024/12/15/[$LATEST]b127d2b3ad4048df85ec127be9da5981"

結果は以下の通りでした。

217 の 217 の一致したレコードの表示
257 レコード (29.9 KB) が 2.0s @ 128 records/s (14.9 KB/s) でスキャンされました

フィールドインデックスを利用 が出力されておらず、CW Logs Insightsのオプティマイザ的なものが自動的に効率的なアクセスパスを選択してくれたことが分かります。

まとめ

大量のアップデートに埋もれて気づくのが遅れてしまったのですが、これはかなり熱いアップデートではないでしょうか?

私は主にサーバーサイドのREST APIなどを開発しており、CW Logs Insightsを利用する際はクエリの条件として以下のようなフィールドを指定する機会が非常に多いです。

  • リクエストID
  • トレースID
  • リクエスト元のユーザーID
  • (Lambdalithな構成の場合)APIのパス&メソッド

これらのフィールドにフィールドインデックスを設定することでクエリの高速化やスキャン量削減によるコスト削減が図れるのは非常に魅力的です。今後は利用必須の機能といっても過言ではないでしょう。業務でもバリバリ活用してきたいです。

参考

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.